package com.xiaozhi.controller;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.hutool.core.lang.UUID;

import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.thoughtworks.xstream.io.path.Path;
import com.xiaozhi.common.web.AjaxResult;
import com.xiaozhi.entity.DevicePic;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.server.ServerWebExchange;

//import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * <pre>
 *  小程序临时素材接口
 *  Created by BinaryWang on 2017/6/16.
 * </pre>
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@RestController
@AllArgsConstructor
@Slf4j
@RequestMapping("/wx/media")
public class WxMaMediaController {
    private final WxMaService wxMaService;
  // 上传目录
    // private static final Path UPLOAD_DIR = Paths.get(System.getProperty("user.dir"), "uploads");
    /**
     * 上传临时素材
     *
     * @return 素材的media_id列表，实际上如果有的话，只会有一个
     */
  /*   @PostMapping("/upload")
    public List<String> uploadMedia(@PathVariable String appid, HttpServletRequest request) throws WxErrorException {
        if (!wxMaService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置，请核实！", appid));
        }

        CommonsMultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());

        if (!resolver.isMultipart(request)) {
            WxMaConfigHolder.remove();//清理ThreadLocal
            return Lists.newArrayList();
        }

        MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
        Iterator<String> it = multiRequest.getFileNames();
        List<String> result = Lists.newArrayList();
        while (it.hasNext()) {
            try {
                MultipartFile file = multiRequest.getFile(it.next());
                File newFile = new File(Files.createTempDir(), file.getOriginalFilename());
                log.info("filePath is ：" + newFile.toString());
                file.transferTo(newFile);
                WxMediaUploadResult uploadResult = wxMaService.getMediaService().uploadMedia(WxMaConstants.KefuMsgType.IMAGE, newFile);
                log.info("media_id ： " + uploadResult.getMediaId());
                result.add(uploadResult.getMediaId());
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        WxMaConfigHolder.remove();//清理ThreadLocal
        return result;
    }*/
     private Mono<AjaxResult> saveFileToDisk(FilePart filePart) {
        // 生成唯一文件名
        //String fileName = UUID.randomUUID().toString() + "-" + filePart.filename();
        //Path filePath = Paths.get("uploads", fileName);
        // 生成唯一的文件名
        String fileName = UUID.randomUUID().toString() + "." + getFileExtension(filePart.filename());
            // 定义文件保存的路径
            // 指定保存目录
            String saveDirectory = "/root/workspace/uploads/"; 
            File directory = new File(saveDirectory);
            if (!directory.exists()) {
                directory.mkdirs();
            }

            //String filePath = "uploads/" + fileName;
            File dest = new File(saveDirectory,fileName);
            // 保存文件 
            log.info("dest:"+dest);
            // 异步保存文件并处理结果
            return filePart.transferTo(dest)
            .subscribeOn(Schedulers.boundedElastic())
            .doOnSubscribe(s -> log.info("开始上传文件: {}", dest.getAbsolutePath()))
            .doOnSuccess(v -> log.info("文件上传成功: {}", dest.getAbsolutePath()))
            .doOnError(e -> {
                log.error("文件上传失败: {}", dest.getAbsolutePath(), e);
                if (dest.exists()) dest.delete();
            })
           /* .map(v -> {
                log.info("文件上传成功，保存路径：" +  directory+fileName);
                
                Map<String, String> picData = new HashMap<>();
                picData.put("fileName", fileName);
                picData.put("url", "http://1.92.66.174:8091/uploads/" + fileName);
                
                AjaxResult result = AjaxResult.success();
                result.put("data", picData);
                //return Mono.just(result);
                return result;
                //return Mono.just(result);
            })*/
            .then(Mono.fromSupplier(() -> {
                log.info("构建响应数据");
                Map<String, String> picData = new HashMap<>();
                picData.put("fileName", fileName);
                picData.put("url", "http://1.92.66.174:8091/uploads/" + fileName);
                AjaxResult result = AjaxResult.success();
                result.put("data", picData);
                return result;
            }))

            .onErrorResume(e -> {
                log.error("文件上传失败: {}", directory, e);
                return Mono.just(AjaxResult.error("上传失败: " + e.getMessage()));
            });
        // 异步保存文件
        //return filePart.transferTo(filePath.toFile())
        //    .thenReturn(filePath.toString());
    }
    
    @PostMapping("/v2/upload")
    public Mono<AjaxResult>  uploadMediaV1(@RequestPart("file") FilePart filePart) throws WxErrorException {
        String fileName = UUID.randomUUID().toString() + "." + getFileExtension(filePart.filename());
            // 定义文件保存的路径
            // 指定保存目录
            String saveDirectory = "/root/workspace/uploads/"; 
            File directory = new File(saveDirectory);
            if (!directory.exists()) {
                directory.mkdirs();
            }

            //String filePath = "uploads/" + fileName;
            File dest = new File(saveDirectory,fileName);
            // 保存文件 
            log.info("dest:"+dest);
            // 异步保存文件并处理结果

            return filePart.transferTo(dest)
            .subscribeOn(Schedulers.boundedElastic())
            .doOnSubscribe(s -> log.info("开始上传文件: {}", dest.getAbsolutePath()))
            .doOnSuccess(v -> log.info("文件上传成功: {}", dest.getAbsolutePath()))
            .doOnError(e -> {
                log.error("文件上传失败: {}", dest.getAbsolutePath(), e);
                if (dest.exists()) dest.delete();
            })
            .doFinally(signalType -> log.info("流终止类型: {}", signalType))
            .flatMap(v -> {
                log.info("构建响应数据");
                Map<String, String> picData = new HashMap<>();
                picData.put("fileName", fileName);
                picData.put("url", "http://1.92.66.174:8091/uploads/" + fileName);
                AjaxResult result = AjaxResult.success();
                result.put("data", picData);
                return Mono.just(result);
            })
            .onErrorResume(e -> {
                log.error("处理上传结果失败", e);
                return Mono.just(AjaxResult.error("上传失败: " + e.getMessage()));
            });
    
           /*  return filePart.transferTo(dest)
            .subscribeOn(Schedulers.boundedElastic())
            .publishOn(Schedulers.boundedElastic()) 
            .doOnSubscribe(s -> log.info("开始上传文件: {}", dest.getAbsolutePath()))
            .doOnSuccess(v -> log.info("文件上传成功: {}", dest.getAbsolutePath()))
            .doOnError(e -> {
                log.error("文件上传失败: {}", dest.getAbsolutePath(), e);
                if (dest.exists()) dest.delete();
            })
            .flatMap(v -> { // 使用 flatMap 替代 map
                log.info("执行 flatMap，构建响应数据");
                Map<String, String> picData = new HashMap<>();
                picData.put("fileName", fileName);
                picData.put("url", "http://1.92.66.174:8091/uploads/" + fileName);
                return Mono.just(AjaxResult.success().put("data", picData));
            })
            .map(v -> {
                log.info("文件上传成功，保存路径：" +  directory+fileName);
                
                Map<String, String> picData = new HashMap<>();
                picData.put("fileName", fileName);
                picData.put("url", "http://1.92.66.174:8091/uploads/" + fileName);
                
                AjaxResult result = AjaxResult.success();
                result.put("data", picData);
                //return Mono.just(result);
                return result;
                //return Mono.just(result);
            })
            .onErrorResume(e -> {
                log.error("文件上传失败: {}", directory, e);
                return Mono.just(AjaxResult.error("上传失败: " + e.getMessage()));
            });*/
            /*return exchange.getMultipartData()
               .flatMap(formData -> {
                    // 获取名为 "file" 的文件部分
                    return Mono.justOrEmpty(formData.getFirst("file"))
                           .cast(FilePart.class)
                           .flatMap(filePart -> {
                                // 生成唯一的文件名
                                String fileName = UUID.randomUUID().toString() + "." + getFileExtension(filePart.filename());
                                // 定义文件保存的路径
                                String filePath = "uploads/" + fileName;
                                File dest = new File(filePath);
                                // 保存文件
                                filePart.transferTo(dest).block();
                                return Mono.just("文件上传成功，保存路径：" + filePath);
                            });
                })
               .defaultIfEmpty("未找到上传的文件");*/

/* 
        if (file.isEmpty()) {
            log.info( "请选择要上传的文件");
            return Mono.just(AjaxResult.error());
        }
        try {
            // 生成唯一的文件名
            String fileName = UUID.randomUUID().toString() + "." + getFileExtension(file.getOriginalFilename());
            // 定义文件保存的路径
            // 指定保存目录
            String saveDirectory = "/root/workspace/uploads/"; 
            File directory = new File(saveDirectory);
            if (!directory.exists()) {
                directory.mkdirs();
            }

            //String filePath = "uploads/" + fileName;
            File dest = new File(saveDirectory,fileName);
            // 保存文件
            file.transferTo(dest);
            log.info( "文件上传成功，保存路径：" + directory+fileName);
            Map<String, String> picData = new java.util.HashMap<>();
            picData.put("fileName",fileName);
            picData.put("url","http://1.92.66.174:8091:/uploads/"+fileName);
            AjaxResult result = AjaxResult.success();
            result.put("data", picData);
            return Mono.just(result);

        } catch (IOException | IllegalStateException e) {
            e.printStackTrace();
            log.info( "文件上传失败");
            log.error(e.getMessage(), e);
            return Mono.just(AjaxResult.error());
        }
        */
    }
    @PostMapping(value = "/v1/upload",consumes = "multipart/form-data")
    public Mono<AjaxResult> uploadMultipleFiles(ServerWebExchange exchange) {
        return exchange.getMultipartData()
        .flatMap(formData -> {
            FilePart filePart = (FilePart) formData.getFirst("file");
            if (filePart == null) {
                return Mono.just(AjaxResult.error("Missing file parameter"));
            }
            
            // 生成文件名和路径
            String originalFilename = filePart.filename();
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String uniqueFilename = UUID.randomUUID() + extension;
            String saveDirectory = "/root/workspace/uploads/"; 
            File directory = new File(saveDirectory);
            if (!directory.exists()) {
                directory.mkdirs();
            }

            //String filePath = "uploads/" + fileName;
            File dest = new File(saveDirectory,uniqueFilename);

            // 执行文件写入并等待完成
            return filePart.transferTo(dest)
                    //.thenReturn(uniqueFilename)
                    .then(Mono.fromCallable(() -> {
                        Map<String, Object> data = new HashMap<>();
                        data.put("filename", uniqueFilename);
                        data.put("url", "http://1.92.66.174:8091/uploads/" + uniqueFilename);
                       // data.put("size", Files.size(dest));
                        
                        AjaxResult result = AjaxResult.success();
                        result.put("data", data);
                        return result;
                    }))
                    /* .map(filename -> {
                        Map<String, Object> data = new HashMap<>();
                        data.put("filename", filename);
                        data.put("url","http://1.92.66.174:8091:/uploads/"+filename);
                        AjaxResult result = AjaxResult.success();
                        result.put("data", data);
                        return result;
                    })*/
                    .onErrorResume(e -> {
                        return Mono.just(AjaxResult.error("Failed to save file: " + e.getMessage()));
                    });
        });
/* 
        return exchange.getMultipartData()
                .flatMapMany(formData -> Flux.fromIterable(formData.get("file")))
                .cast(FilePart.class)
                .flatMap(this::saveFile)
                .collectList()
                .map(filenames -> {
                    Map<String, Object> data = new HashMap<>();
                    data.put("filenames", filenames.get(0));
                    data.put("url","http://1.92.66.174:8091:/uploads/"+filenames.get(0));
                    AjaxResult result = AjaxResult.success();
                    result.put("data", data);

                    return result; // AjaxResult.success("Files uploaded successfully", data);
                })
                .onErrorResume(e -> Mono.just(AjaxResult.error("Failed to upload files: " + e.getMessage())));*/
    }

    // 保存文件到磁盘
    private Mono<String> saveFile(FilePart filePart) {
        // 生成唯一文件名
        String originalFilename = filePart.filename();
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String uniqueFilename = UUID.randomUUID() + extension;
        String saveDirectory = "/root/workspace/uploads/"; 
        File directory = new File(saveDirectory);
        if (!directory.exists()) {
            directory.mkdirs();
        }

        //String filePath = "uploads/" + fileName;
        File dest = new File(saveDirectory,uniqueFilename);

        // 保存文件
        return filePart.transferTo(dest)
                .thenReturn(uniqueFilename)
                .onErrorMap(e -> new IOException("Failed to save file: " + e.getMessage(), e));
    }

    /**
     * 下载临时素材
     */
    @GetMapping("/download/{mediaId}")
    public File getMedia(@PathVariable String appid, @PathVariable String mediaId) throws WxErrorException {
        if (!wxMaService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置，请核实！", appid));
        }
        File media = wxMaService.getMediaService().getMedia(mediaId);
        WxMaConfigHolder.remove();//清理ThreadLocal
        return media;
    }
    private String getFileExtension(String fileName) {
        if (fileName == null) {
            return "";
        }
        int lastIndex = fileName.lastIndexOf(".");
        if (lastIndex == -1) {
            return "";
        }
        return fileName.substring(lastIndex + 1);
    }
}
